#ifndef _ASMARM_PGTABLE_H
#define _ASMARM_PGTABLE_H

#include <linux/mm_types.h>

#include <asm-generic/4level-fixup.h>
#include <asm-generic/memory_model.h>
#include <asm/pgtable-hwdef.h>
#include <asm/memory.h>
#include <asm/tlbflush.h>

extern struct mm_struct init_mm;

#define VMALLOC_END       (0xfe000000)

#ifndef VMALLOC_START
#define VMALLOC_OFFSET		(8*1024*1024)
#define VMALLOC_START		(((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#endif

// #define cpu_set_pte_ext			__cpu_fn(CPU_NAME,_set_pte_ext)
#define cpu_set_pte_ext			cpu_v7_set_pte_ext

#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)

#define set_pte_at(mm,addr,ptep,pteval) do { \
	set_pte_ext(ptep, pteval, (addr) >= TASK_SIZE ? 0 : PTE_EXT_NG); \
 } while (0)

extern pgprot_t		pgprot_kernel;
#define PAGE_KERNEL		pgprot_kernel



#define PTRS_PER_PTE		512

#define PMD_SHIFT		21
#define PGDIR_SHIFT		21

#define PMD_SIZE		(1UL << PMD_SHIFT)
#define PMD_MASK		(~(PMD_SIZE-1))
#define PGDIR_SIZE		(1UL << PGDIR_SHIFT)
#define PGDIR_MASK		(~(PGDIR_SIZE-1))

#define pfn_pte(pfn,prot)	(__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
#define mk_pte(page,prot)	pfn_pte(page_to_pfn(page),prot)

#define pgd_present(pgd)	(1)
#define set_pgd(pgd,pgdp)	do { } while (0)

#define FIRST_USER_PGD_NR	1
#define USER_PTRS_PER_PGD	((TASK_SIZE/PGDIR_SIZE) - FIRST_USER_PGD_NR)

#define copy_pmd(pmdpd,pmdps)		\
	do {				\
		pmdpd[0] = pmdps[0];	\
		pmdpd[1] = pmdps[1];	\
		flush_pmd_entry(pmdpd);	\
	} while (0)

#define __pte_index(addr)	(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))

#define __pgprot(x)     (x)

#define pmd_none(pmd)		(!pmd_val(pmd))

#define pmd_clear(pmdp)			\
	do {				\
		pmdp[0] = __pmd(0);	\
		pmdp[1] = __pmd(0);	\
		clean_pmd_entry(pmdp);	\
	} while (0)

static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
	unsigned long ptr;

	ptr = pmd_val(pmd) & ~(PTRS_PER_PTE * sizeof(void *) - 1);
	ptr += PTRS_PER_PTE * sizeof(void *);

	return __va(ptr);
}

#define pgd_none(pgd)		(0)
#define pte_offset_kernel(dir,addr)	(pmd_page_vaddr(*(dir)) + __pte_index(addr))

#define pmd_present(pmd)	(pmd_val(pmd))

#define pgd_index(addr)		((addr) >> PGDIR_SHIFT)
#define pgd_offset(mm, addr)	((mm)->pgd+pgd_index(addr))
#define pgd_offset_k(addr)	pgd_offset(&init_mm, addr)

#define pmd_offset(dir, addr)	((pmd_t *)(dir))

#define PTRS_PER_PGD		2048
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];

/*
 * section address mask and size definitions.
 */
#define SECTION_SHIFT		20
#define SECTION_SIZE		(1UL << SECTION_SHIFT)
#define SECTION_MASK		(~(SECTION_SIZE-1))

/*
 * "Linux" PTE definitions.
 *
 * We keep two sets of PTEs - the hardware and the linux version.
 * This allows greater flexibility in the way we map the Linux bits
 * onto the hardware tables, and allows us to have YOUNG and DIRTY
 * bits.
 *
 * The PTE table pointer refers to the hardware entries; the "Linux"
 * entries are stored 1024 bytes below.
 */
#define L_PTE_PRESENT		(1 << 0)
#define L_PTE_YOUNG		(1 << 1)
#define L_PTE_FILE		(1 << 2)	/* only when !PRESENT */
#define L_PTE_DIRTY		(1 << 6)
#define L_PTE_WRITE		(1 << 7)
#define L_PTE_USER		(1 << 8)
#define L_PTE_EXEC		(1 << 9)
#define L_PTE_SHARED		(1 << 10)	/* shared(v6), coherent(xsc3) */

/*
 * These are the memory types, defined to be compatible with
 * pre-ARMv6 CPUs cacheable and bufferable bits:   XXCB
 */
#define L_PTE_MT_UNCACHED	(0x00 << 2)	/* 0000 */
#define L_PTE_MT_BUFFERABLE	(0x01 << 2)	/* 0001 */
#define L_PTE_MT_WRITETHROUGH	(0x02 << 2)	/* 0010 */
#define L_PTE_MT_WRITEBACK	(0x03 << 2)	/* 0011 */
#define L_PTE_MT_MINICACHE	(0x06 << 2)	/* 0110 (sa1100, xscale) */
#define L_PTE_MT_WRITEALLOC	(0x07 << 2)	/* 0111 */
#define L_PTE_MT_DEV_SHARED	(0x04 << 2)	/* 0100 */
#define L_PTE_MT_DEV_NONSHARED	(0x0c << 2)	/* 1100 */
#define L_PTE_MT_DEV_WC		(0x09 << 2)	/* 1001 */
#define L_PTE_MT_DEV_CACHED	(0x0b << 2)	/* 1011 */
#define L_PTE_MT_MASK		(0x0f << 2)

#define pte_present(pte)	(pte_val(pte) & L_PTE_PRESENT)
#define pte_none(pte)		(!pte_val(pte))

#define pmd_page(pmd)		pfn_to_page(__phys_to_pfn(pmd_val(pmd)))

#define __pte_map(dir,km)	pmd_page_vaddr(*(dir))
#define __pte_index(addr)	(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
#define pte_offset_map(dir,addr)	(__pte_map(dir, KM_PTE0) + __pte_index(addr))

#define PTE_BIT_FUNC(fn,op) \
static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }

PTE_BIT_FUNC(wrprotect, &= ~L_PTE_WRITE);
PTE_BIT_FUNC(mkwrite,   |= L_PTE_WRITE);
PTE_BIT_FUNC(mkclean,   &= ~L_PTE_DIRTY);
PTE_BIT_FUNC(mkdirty,   |= L_PTE_DIRTY);
PTE_BIT_FUNC(mkold,     &= ~L_PTE_YOUNG);
PTE_BIT_FUNC(mkyoung,   |= L_PTE_YOUNG);

#include <asm-generic/pgtable.h>

#endif
